#include "BigInt.hpp"

/**
 * author: pangchao
 * date: 2025-05-28
 * 
*/
class ECC
{
public:
    class E
    {
    public:
        E(long long a = 0, long long b = 0, long long p = 0)
        :a_(a), b_(b),p_(p){}
        E(const E& e)
        {
            a_ = e.a_;
            b_ = e.b_;
            p_ = e.p_;
        }
        E& operator=(const E& e)
        {
            a_ = e.a_;
            b_ = e.b_;
            p_ = e.p_;
        }

    public:
        BigInt a_;
        BigInt b_;
        BigInt p_;
    };
    class Point
    {
    public:
        Point(const int left = 0, const int& right = 0)
        :left_(left), right_(right)
        {}
        Point(const Point& point)
        {
            left_ = point.left_;
            right_ = point.right_;
        }
        bool operator==(const Point& b) const
        {
            return ((left_ == b.left_) && right_ == b.right_);
        }
        friend std::ostream& operator<<(std::ostream& out, const Point& point)
        {
            out << "(" << point.left_ << ", " << point.right_ << ")";
            return out;
        }

    public:
        BigInt left_;
        BigInt right_;
    };
    // 椭圆曲线的加法 把 b 加到 a上
    Point Plus(const Point& a, const Point& b) const
    {
        Point result;
        BigInt k = 0;
        if (a == b)
        {
            // k = (3*x1*x1 + a) / (2*y1) mod p
            
            // 分子
            BigInt mf = a.left_ * a.left_ * 3 + e_.a_;

            // 分母
            BigInt mo = a.right_ * 2;

            // 分母求 p-2 次方 mod p
            BigInt rop = mo.powm(e_.p_ - 2, e_.p_);

            // k = rop * mf mod p
            k = rop * mf % e_.p_;
        }
        else
        {
            // k = (y2 - y1) / (x2 - x1) mod p
            BigInt mf = b.right_ - a.right_;
            // std::cout << "mf: " << mf << std::endl;
            BigInt mo = b.left_ - a.left_;
            // std::cout << "mo: " << mo << std::endl;
            // 分母求 p-2 次方 mod p
            // std::cout << "p-2: " << e_.p_ - 2 << " p: " << e_.p_ << std::endl;
            BigInt rop = mo.powm(e_.p_ - 2, e_.p_);
            // std::cout << "rop: " << rop << std::endl;
            // k = rop * mf mod p
            k = rop * mf % e_.p_;
        }
        // std::cout << "k: " << k << std::endl;
        // x3 = k*k - x1 - x2 mod p
        result.left_ = (k * k - a.left_ - b.left_) % e_.p_;

        // y3 = k*(x1 - x3) - y1 mod p
        result.right_ = (k * (a.left_ - result.left_) - a.right_) % e_.p_;
        return result;
    }
public:
    ECC()
    {
        // 这里使用bit币的加密曲线 标准为 Secp256k1
        // https://en.bitcoin.it/wiki/Secp256k1
        e_.a_ = 0;
        e_.b_ = 7;
        e_.p_.SetBigInt("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16);

        // 选定基点 G
        G_.left_.SetBigInt("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16);
        G_.right_.SetBigInt("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16);

        // 基点阶数 n
        n_.SetBigInt("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16);
        std::cout << "BitInitE done" << std::endl;
    }
    void ECCCreateKey(BigInt& k, Point& K) const
    {
        // 因为我的设想是 每个椭圆曲线都是一个类 所以这里不需要传入 关于椭圆曲线的东西
        // 1. 选定一个私有密钥 k
        srand((unsigned)time(NULL));
        k = BigInt::GetRandomBigInt(16) % n_;
        Point tmp = G_;
        for (BigInt i = 1; i < k; ++i)
        {
            tmp = Plus(tmp, G_);
        }
        K = tmp;
        std::cout << "公钥K: " << K << std::endl;
        std::cout << "私钥k: " << k << std::endl;
    }
    void ECCEncode(const BigInt& content, const Point& K, BigInt& r, Point& C1, Point& C2) const
    {
        BigInt tmp = content.pow(3) + e_.a_ * content + e_.b_;
        Point M;
        M.left_ = content;
        M.right_ = e_.p_ - tmp.tonelli(tmp, e_.p_);

        std::cout << "明文坐标: " << M << std::endl;
        srand((unsigned int)time(NULL));
        r = BigInt::GetRandomBigInt(16) % n_;
        C1 = K;
        C2 = G_;
        for (BigInt i = 1; i < r; ++i)
        {
            C1 = Plus(C1, K);
            C2 = Plus(C2, G_);
        }
        C1 = Plus(C1, M);

    }
    BigInt ECCDecode(const BigInt& k, const Point& C1, const Point& C2) const
    {
        // P = (x, y) -P = (x, (-y) % p)
        // 1. 计算 C1 - kC2
        Point tmp = C2;
        for (BigInt i = 1; i < k;++i)
        {
            tmp = Plus(tmp, C2);
        }
        tmp.right_ = e_.p_ - tmp.right_;
        Point result = Plus(C1, tmp);
        // 2. result 的 左坐标 即为 明文
        std::cout << "解密明文坐标: " << result << std::endl;
        return result.left_;
    }
    void PrintInfo()
    {
        std::cout << "本次椭圆曲线加密算法信息:" << std::endl;
        std::cout << "使用曲线: y^2 = x^3 + " << e_.a_ << "x + " << e_.b_ << " mod " << e_.p_ << std::endl;
        std::cout << "选择基点G: " << G_ << std::endl;
        std::cout << "基点阶数n: " << n_ << std::endl;
    }   
    BigInt StrToBigInt(const std::string& content)
    {
        BigInt result;
        BigInt tmp = 1;
        for (size_t i = 0; i < content.length(); ++i)
        {
            result += tmp * ((int)content[i] + 128);
            tmp *= 256;
        }
        return result;
    }
    std::string BigIntToStr(BigInt content)
    {
        std::string result;
        while(content != 0)
        {
            result += (char)((content % 256).ToInt() - 128);
            content /= 256;
        }
        return result;
    }
private:
    E e_;
    Point G_;
    BigInt n_;
};